package com.zx.notification.producer.service.impl;

import com.zx.core.enums.StatusEnum;
import com.zx.core.wrapper.ObjectWrapper;
import com.zx.core.wrapper.ResponseWrapper;
import com.zx.feign.client.SystemClient;
import com.zx.feign.client.dto.GetSysUserResponseDTO;
import com.zx.notification.common.constants.NotificationConstants;
import com.zx.notification.common.dao.NotificationChannelsConfigMapper;
import com.zx.notification.common.dao.NotificationPushTasksMapper;
import com.zx.notification.common.dao.NotificationTemplatesMapper;
import com.zx.notification.common.dao.SystemNotificationsMapper;
import com.zx.notification.common.enums.BizTypeEnum;
import com.zx.notification.common.enums.ConsumeModeEnum;
import com.zx.notification.common.enums.PushStatusEnum;
import com.zx.notification.common.event.NotificationCreatedEvent;
import com.zx.notification.common.mapstruct.SystemNotificationsMapStructMapper;
import com.zx.notification.common.model.NotificationChannelsConfig;
import com.zx.notification.common.model.NotificationPushTasks;
import com.zx.notification.common.model.NotificationTemplates;
import com.zx.notification.common.model.SystemNotifications;
import com.zx.notification.common.param.SystemNotificationsParam;
import com.zx.notification.common.utils.NotificationNoUtils;
import com.zx.notification.producer.config.NotificationConfig;
import com.zx.notification.producer.service.ISystemNotificationsService;
import com.zx.redis.block.service.RedisBlockService;
import com.zx.validation.annotation.ParameterValidation;
import com.zx.validation.annotation.ParameterValidations;
import com.zx.validation.enums.ValidateRulesEnum;
import com.zx.validation.groups.Create;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * description: 系统通知服务实现类 <br>
 * create: 2025-10-15
 * </p>
 *
 * @author zhou  xun
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SystemNotificationsServiceImpl implements ISystemNotificationsService {

    private final SystemNotificationsMapper systemNotificationsMapper;
    private final NotificationTemplatesMapper notificationTemplatesMapper;
    private final NotificationChannelsConfigMapper notificationChannelsConfigMapper;
    private final NotificationPushTasksMapper notificationPushTasksMapper;
    private final SystemClient systemClient;
    private final ApplicationEventPublisher eventPublisher;
    private final RedisBlockService redisBlockService;
    private final NotificationConfig notificationConfig;
    private final SystemNotificationsMapStructMapper mapStructMapper;


    private static NotificationPushTasks getNotificationPushTasks(NotificationTemplates notificationTemplates, SystemNotifications systemNotifications, String channel, NotificationChannelsConfig channelsConfig) {
        NotificationPushTasks pushTask = new NotificationPushTasks();
        pushTask.setId(NotificationNoUtils.generateNotificationId());
        pushTask.setNotificationId(systemNotifications.getId());
        pushTask.setChannelType(channel);
        pushTask.setMqType(channelsConfig.getMqType());
        pushTask.setConsumeMode(notificationTemplates.getConsumeMode());
        ConsumeModeEnum consumeMode = ConsumeModeEnum.getEnumByCode(notificationTemplates.getConsumeMode());
        if (consumeMode == ConsumeModeEnum.QUEUE) {
            pushTask.setDestinationName(channelsConfig.getQueueName());
            pushTask.setDestinationType(channelsConfig.getQueueDestinationType());
        }
        if (consumeMode == ConsumeModeEnum.TOPIC) {
            pushTask.setDestinationName(channelsConfig.getTopicName());
            pushTask.setDestinationType(channelsConfig.getTopicDestinationType());
        }
        pushTask.setRoutingKey(channelsConfig.getQueueRoutingKey());
        pushTask.setTag(channelsConfig.getQueueTag());
        pushTask.setPushStrategy(channelsConfig.getPushStrategy());
        pushTask.setStatus(PushStatusEnum.PENDING.getCode());
        // 初始化重试次数为0
        pushTask.setRetryCount(0);
        pushTask.setCreateTime(LocalDateTime.now());
        return pushTask;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @ParameterValidations({
            @ParameterValidation(index = 0, desc = "系统通知参数", rules = {ValidateRulesEnum.OBJECT}, groups = {Create.class})
    })
    public ResponseWrapper addSystemNotification(SystemNotificationsParam systemNotificationsParam) {
        ResponseWrapper result = new ResponseWrapper();
        try {
            // 验证参数
            validateSystemNotificationParams(systemNotificationsParam, true);

            // 创建通知信息对象
            SystemNotifications systemNotifications = mapStructMapper.systemNotificationsParamToSystemNotifications(systemNotificationsParam);
            systemNotifications.setId(NotificationNoUtils.generateNotificationId());

            // 设置创建信息
            systemNotifications.setCreateTime(LocalDateTime.now());

            // 如果没有设置过期时间，则设置默认过期时间（创建时间+默认天数）
            if (systemNotifications.getExpireTime() == null) {
                // 使用配置的默认天数
                systemNotifications.setExpireTime(systemNotifications.getCreateTime().plusDays(notificationConfig.getDefaultExpireDays()));
            }

            // 插入通知到数据库
            systemNotificationsMapper.insertSelective(systemNotifications);

            // 为每个渠道创建推送任务
            createPushTasksForNotification(systemNotifications);

            // 发布通知创建事件
            eventPublisher.publishEvent(new NotificationCreatedEvent(this, systemNotifications.getId()));
        } catch (Exception e) {
            log.error("添加系统通知异常", e);
            result.fail("添加系统通知异常: " + e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

    /**
     * 为通知创建推送任务
     *
     * @param systemNotifications 系统通知
     * @return 创建的推送任务列表
     */
    private List<NotificationPushTasks> createPushTasksForNotification(SystemNotifications systemNotifications) {
        // 创建推送任务列表
        List<NotificationPushTasks> pushTasks = new ArrayList<>();
        try {
            // 获取目标渠道列表
            if (!StringUtils.hasText(systemNotifications.getTargetChannels())) {
                log.warn("通知[{}]的目标渠道为空，跳过创建推送任务", systemNotifications.getId());
                return pushTasks;
            }
            String[] targetChannels = systemNotifications.getTargetChannels().split(",");

            // 获取模板信息（优先从缓存获取）
            NotificationTemplates notificationTemplates = getTemplateFromCache(systemNotifications.getTemplateCode());


            // 循环为每个渠道创建推送任务对象
            for (String channel : targetChannels) {
                // 获取渠道配置（优先从缓存获取）
                NotificationChannelsConfig channelsConfig = getChannelConfigFromCache(channel);
                if (channelsConfig == null) {
                    log.warn("渠道[{}]配置不存在，跳过创建推送任务", channel);
                    continue;
                }

                // 创建推送任务
                NotificationPushTasks pushTask = getNotificationPushTasks(notificationTemplates, systemNotifications, channel, channelsConfig);

                pushTasks.add(pushTask);
            }

            // 批量插入推送任务
            if (!pushTasks.isEmpty()) {
                notificationPushTasksMapper.batchInsert(pushTasks);
                log.info("为通知[{}]批量创建了{}个推送任务", systemNotifications.getId(), pushTasks.size());
            } else {
                log.warn("通知[{}]没有创建任何推送任务", systemNotifications.getId());
            }

        } catch (Exception e) {
            log.error("为通知[{}]创建推送任务时发生错误", systemNotifications.getId(), e);
            throw new RuntimeException("创建推送任务失败: " + e.getMessage(), e);
        }

        return pushTasks;
    }

    /**
     * 从缓存获取渠道配置，如果缓存中不存在则从数据库获取并存入缓存
     *
     * @param channelType 渠道类型
     * @return 渠道配置
     */
    private NotificationChannelsConfig getChannelConfigFromCache(String channelType) {
        String cacheKey = NotificationConstants.RedisCache.CHANNEL_CONFIG_CACHE_KEY_PREFIX + channelType;
        NotificationChannelsConfig config = redisBlockService.getCachedObject(cacheKey, NotificationChannelsConfig.class);
        if (config == null) {
            // 缓存中不存在，从数据库获取
            config = notificationChannelsConfigMapper.selectByChannelType(channelType);
            if (config != null) {
                // 存入缓存，设置过期时间
                redisBlockService.setCacheObject(cacheKey, config, NotificationConstants.RedisCache.CHANNEL_CONFIG_CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
            }
        }

        return config;
    }

    /**
     * 从缓存获取模板，如果缓存中不存在则从数据库获取并存入缓存
     *
     * @param templateCode 模板代码
     * @return 模板
     */
    private NotificationTemplates getTemplateFromCache(String templateCode) {
        String cacheKey = NotificationConstants.RedisCache.TEMPLATE_CACHE_KEY_PREFIX + templateCode;
        NotificationTemplates template = redisBlockService.getCachedObject(cacheKey, NotificationTemplates.class);
        if (template == null) {
            // 缓存中不存在，从数据库获取
            template = notificationTemplatesMapper.selectByTemplateCode(templateCode);
            if (template != null) {
                // 存入缓存，设置过期时间
                redisBlockService.setCacheObject(cacheKey,
                        template,
                        NotificationConstants.RedisCache.TEMPLATE_CACHE_EXPIRE_TIME,
                        TimeUnit.SECONDS);
            }
        }

        return template;
    }

    /**
     * 验证系统通知参数
     *
     * @param systemNotificationsParam 系统通知参数
     * @param isCreate                 是否为创建操作
     */
    private void validateSystemNotificationParams(SystemNotificationsParam systemNotificationsParam, boolean isCreate) {
        // 验证业务类型
        BizTypeEnum bizTypeEnum = BizTypeEnum.getEnumByCode(systemNotificationsParam.getBizType());
        if (bizTypeEnum == null) {
            throw new IllegalArgumentException("业务类型错误");
        }

        // 调用系统用户服务查询用户信息
        ObjectWrapper<GetSysUserResponseDTO> userInfoById = systemClient.getUserInfoById(systemNotificationsParam.getUserId());
        if (userInfoById.verifyFail()) {
            log.error("获取用户信息失败，用户ID: {}, 错误码: {}, 错误信息: {}",
                    systemNotificationsParam.getUserId(),
                    userInfoById.getCode(),
                    userInfoById.getMsg());
            throw new RuntimeException("获取接收用户信息失败: " + userInfoById.getMsg());
        }

        if (userInfoById.getData() == null) {
            throw new IllegalArgumentException("接收用户不存在");
        }

        // 验证接收用户状态
        StatusEnum statusEnum = StatusEnum.getEnumByCode(userInfoById.getData().status());
        if (statusEnum != StatusEnum.ENABLE) {
            throw new IllegalArgumentException("接收用户未启用");
        }

        // 验证模版是否存在, 验证模版状态（优先从缓存获取）
        NotificationTemplates notificationTemplates = getTemplateFromCache(systemNotificationsParam.getTemplateCode());
        if (notificationTemplates == null) {
            throw new IllegalArgumentException("模板不存在");
        }

        StatusEnum notificationTemplatesEnum = StatusEnum.getEnumByCode(notificationTemplates.getStatus());
        if (notificationTemplatesEnum != StatusEnum.ENABLE) {
            throw new IllegalArgumentException("模板未启用");
        }

        // 验证如果目标渠道是否在模板支持渠道中
        String[] targetChannels = systemNotificationsParam.getTargetChannels().split(",");
        String[] supportedChannels = notificationTemplates.getSupportedChannels().split(",");

        // 使用 Stream API
        List<String> unsupportedChannels = Arrays.stream(targetChannels)
                .filter(element -> !Arrays.asList(supportedChannels).contains(element))
                .toList();
        if (!unsupportedChannels.isEmpty()) {
            throw new IllegalArgumentException("存在模板不支持的渠道: " + String.join(",", unsupportedChannels));
        }

        // 验证过期时间是否晚于创建时间
        if (systemNotificationsParam.getExpireTime() != null && systemNotificationsParam.getCreateTime() != null) {
            if (systemNotificationsParam.getExpireTime().isBefore(systemNotificationsParam.getCreateTime())) {
                throw new IllegalArgumentException("过期时间不能早于创建时间");
            }

            // 验证过期时间是否超过创建时间太远（最多一年）
            LocalDateTime maxExpireTime = systemNotificationsParam.getCreateTime().plusYears(1);
            if (systemNotificationsParam.getExpireTime().isAfter(maxExpireTime)) {
                throw new IllegalArgumentException("过期时间不能超过创建时间一年以上");
            }
        }
    }

    /**
     * 获取通知相关的所有推送任务
     *
     * @param notificationId 通知ID
     * @return 推送任务列表
     */
    public List<NotificationPushTasks> getPushTasksByNotificationId(String notificationId) {
        return notificationPushTasksMapper.selectByNotificationId(notificationId);
    }

    /**
     * 重新发送通知
     *
     * @param notificationId 通知ID
     * @return 响应结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseWrapper resendNotification(String notificationId) {
        ResponseWrapper result = new ResponseWrapper();
        try {
            // 验证通知是否存在
            SystemNotifications notification = systemNotificationsMapper.selectByPrimaryKey(notificationId);
            if (notification == null) {
                result.fail("通知不存在");
                return result;
            }

            // 检查是否已存在推送任务
            List<NotificationPushTasks> existingTasks = getPushTasksByNotificationId(notificationId);
            if (existingTasks.isEmpty()) {
                // 如果没有推送任务，则创建推送任务
                createPushTasksForNotification(notification);
                log.info("为通知[{}]创建推送任务并触发发送", notificationId);
            }

            // 发布通知创建事件，触发发送
            eventPublisher.publishEvent(new NotificationCreatedEvent(this, notificationId));
            result.setMsg("通知发送请求已提交");
        } catch (Exception e) {
            log.error("发送通知[{}]时发生错误", notificationId, e);
            result.fail("发送通知异常: " + e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }
}